/**
 * Public methods collection. 
 */
methods = {
		
		/**
		 * Creates imgcombo component on top of given SELECT elements.
		 * 
		 * @param options - object literal that will be extended over the plugin's defaults
		 * @returns unmodified jQuery selection from context to maintain chainability
		 */
		init: function(options) {
			/* Remember to maintain the chainability */
			return this.each(function() {
				
				/* Gets jQuery representation of context element and its associated data */
				var $this = $(this);
				
				/* If current element is not SELECT or it is not already initialized... */
				if (!$this.is('select') || ($.hasData(this) && $this.data(namespace))) {
					return;
				}
				
				/* Extend options literal object over plugin's defaults */
				options = $.extend({}, $.fn.imgcombo.defaults, options);
				
				/* Initialize components */
				components = {
					/* Original SELECT element will be still present in the DOM but hidden */
					original: $this,
					/* Wrapper represents whole SELECT alternative displayed to user */
					wrapper: $this.before(options.createElement('wrapper')).prev(),
					/* Caption component displays currently selected OPTION */
					caption: options.createElement('caption').appendTo($this.prev()),
					/* Arrow button component next to caption */
					arrow: options.createElement('arrow').appendTo($this.prev()),
					/* Menu with available OPTION items */
					menu: options.createElement('menu').appendTo($this.prev())
				};
				
				/* Rename hidden option tags to be not processed by initialization function */
				components.original.children('option').each(function() {
					var $this = $(this);
					if ($this.css('display') == 'none' || $this.css('visibility') == 'hidden') {
						var $new = $this.after('<optionhidden />').next();
						$new.html($this.html());
	        			for (var i in this.attributes) {
	            			$new.attr(this.attributes[i].nodeName, this.attributes[i].nodeValue);
	        			}
	        			$this.remove();
					}
				});
				
				/*
				 * Helping data object which will be stored in all components of imgcombo.
				 * Dataset extends components object literal, so it includes components attributes automatically
				 * it also extends privates object literal, so it contains also all private methods.
				 */
				var dataset = $.extend({}, components, $.extend({}, privates, {
					
					/* Options object for this imgcombo */
					options: options,
					
					/* Items of the menu representing OPTIONS of the original SELECT */
					items: $.map(components.original.children('option'), function(element) {
						/* Menu item element */
						var menuitem = options.createElement('item');
						if ($(element).text()) {
							/* Inside caption elements - for measuring reasons */
							menuitem.append(options.createElement('itemtext').text($(element).text()));
						}
						/* Appended in menu component */
						return menuitem.appendTo(components.menu);
					}),
					
					/* Looks for data-* attribute of OPTION elements of original SELECT and determines if at least one has defined icon */
					useIcons: components.original.children('option[data-' + options.data.icon + ']').length > 0
					
				}));
				
				/* Sets data for all components */
				$.each(components, function(index, component) {
					component.data(namespace, dataset);
				});
				
				/* Sets data for all items */
				$.each(dataset.items, function(index, item) {
					item.data(namespace, dataset);
				});
				
				/* Layout created sub-components */
				dataset.layout();
				
			});
		},
		
		/**
		 * Destroys imgcombo component, unregisters triggers, erases data and removes additional DOM
		 * elements from HTML document. Appearance and behavior of original SELECT should be restored
		 * to state before calling of initialization.
		 * 
		 * @returns unmodified jQuery selection from context to maintain chainability
		 */
		destroy: function() {
			/* Remember to maintain the chainability */
			return this.each(function() {
				var data = $(this).data(namespace);
				
				/* Check if component is initialized */
				if (!data) {
					return;
				}
				
				/* Unbind event handlers */
				data.disable(true);
				
				/* Remove data from all components */
				$.each(components, function(index, component) {
					component.removeData(namespace);
				});
				
				/* Remove data from all menu items */
				$.each(data.items, function(index, item) {
					item.removeData(namespace);
				});
				
				/* Remove wrapper component with all its children */
				data.wrapper.remove();
				
				/* Recreate renamed option tags excluded from processing */
				data.original.children('optionhidden').each(function() {
					var $this = $(this),
					$new = $this.after('<option />').next();
					$new.html($this.html());
        			for (var i in this.attributes) {
            			$new.attr(this.attributes[i].nodeName, this.attributes[i].nodeValue);
        			}
        			$this.remove();
				});
				
				/* Remove place-holders for hidden option tags */
				data.original.find('optionhidden').each(function() {
					var $this = $(this);
					$this.after($this.html().replace(/optionhidden/g, 'option')).remove();
				});
				
				/* Show original SELECT */
				data.original.show();
			});
		},
		
		/**
		 * Enables component.
		 * 
		 * @returns unmodified jQuery selection from context to maintain chainability
		 */
		enable: function() {
			/* Remember to maintain the chainability */
			return this.each(function() {
				var data = $(this).data(namespace);
				
				/* Check if component is initialized */
				if (!data) {
					return;
				}
				
				data.enable();
				
			});
		},
		
		/**
		 * Disables component.
		 * 
		 * @returns unmodified jQuery selection from context to maintain chainability
		 */
		disable: function() {
			/* Remember to maintain the chainability */
			return this.each(function() {
				var data = $(this).data(namespace);
				
				/* Check if component is initialized */
				if (!data) {
					return;
				}
				
				data.disable();
				
			});
		}
		
},

